package com.ruoyi.project.app.controller;

import com.ruoyi.project.app.domain.Order;
import com.ruoyi.project.app.mapper.OrderMapper;
import com.ruoyi.project.app.mapper.ProductMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;

@Component
@Slf4j
public class MergeQueue {

    ExecutorService executorService = Executors.newCachedThreadPool();

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private ProductMapper productMapper;

    private Map<Integer,LinkedBlockingQueue<RequestPromise>> map = new ConcurrentHashMap<>();


    public Result offer(Order request) throws InterruptedException {
        LinkedBlockingQueue<RequestPromise> queue = map.computeIfAbsent(request.getProductId(),(k)-> new LinkedBlockingQueue<>());

        RequestPromise requestPromise = new RequestPromise(request,Thread.currentThread());

        boolean enqueueSuccess = queue.offer(requestPromise, 100, TimeUnit.MILLISECONDS);
        if (!enqueueSuccess) {
            return new Result(false, "系统繁忙");
        }
        requestPromise.await(300);

        if (requestPromise.getResult() == null) {
            //可能需要回滚扣掉的库存 todo
            return new Result(false, "等待超时");
        }
        return requestPromise.getResult();
    }

    @PostConstruct
    public void mergeJob() {
        new Thread(() -> {
            while (true) {
                if (map.isEmpty()) {
                    try {
                        Thread.sleep(100);
                        continue;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                Set<Integer> productIds = map.keySet();

                for (Integer productId : productIds) {
//                    executorService.submit(()->{
                        LinkedBlockingQueue<RequestPromise> queue = map.get(productId);
                        if(queue.isEmpty()){
                            return;
                        }
                        List<RequestPromise> list = new ArrayList<>();
                        int batchSize = queue.size();
                        for (int i = 0; i < batchSize; i++) {
                            list.add(queue.poll());
                        }

                        log.info(Thread.currentThread().getName() + ":合并扣减库存:" + list);

                        int sum = list.stream().mapToInt(e -> e.getRequest().getBuyNum()).sum();

                        //扣减库存，生成订单
                        boolean f = productMapper.decrStock(list.get(0).getRequest().getProductId(), sum);
                        List<Order> orders = list.stream().map(v -> v.getRequest()).collect(Collectors.toList());
                        int affect = orderMapper.saveBatch(orders);
                        if(!f){
                            log.error("扣减库存失败");
                        }
                        if(affect!=orders.size()){
                            log.error("保存订单失败");
                        }
                        // notify user
                        list.forEach(requestPromise -> {
                            requestPromise.setResult(new Result(true, "ok"));
                            requestPromise.signal();
                        });
//                    });
                }
            }
        }, "mergeThread").start();
    }



    class RequestPromise {
        private Order request;
        private Result result;
        private Thread thread;

        public RequestPromise(Order request,Thread thread) {
            this.request = request;
            this.thread = thread;
        }


        public void await(long mills){
            LockSupport.parkNanos(mills*1000000);
        }

        public void signal(){
            LockSupport.unpark(thread);
        }

        public Order getRequest() {
            return request;
        }

        public void setRequest(Order request) {
            this.request = request;
        }

        public Result getResult() {
            return result;
        }

        public void setResult(Result result) {
            this.result = result;
        }

        @Override
        public String toString() {
            return "RequestPromise{" +
                    "userRequest=" + request +
                    ", result=" + result +
                    '}';
        }
    }

    class Result {
        private Boolean success;
        private String msg;


        public Result(boolean success, String msg) {
            this.success = success;
            this.msg = msg;
        }

        public boolean isSuccess() {
            return success;
        }

        public void setSuccess(boolean success) {
            this.success = success;
        }

        public String getMsg() {
            return msg;
        }

        public void setMsg(String msg) {
            this.msg = msg;
        }

        @Override
        public String toString() {
            return "Result{" +
                    "success=" + success +
                    ", msg='" + msg + '\'' +
                    '}';
        }
    }
}
